﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using Lime49.UI;
using LockCrypt.DAL;
using LockCrypt.Core;
using System.Windows.Interop;

namespace LockCrypt {
    /// <summary>
    /// Window for adding/editing templates.
    /// </summary>
    public partial class WinAddTemplate : Window {
        private readonly bool _editing = false;
        private readonly string _originalName = string.Empty;

        public static readonly DependencyProperty SelectedTemplateProperty = DependencyProperty.Register("SelectedTemplate", typeof(ITemplate), typeof(WinAddTemplate), new PropertyMetadata(new Template()));

        /// <summary>
        /// Gets or sets the template being edited.
        /// </summary>
        /// <value>The template.</value>
        public ITemplate SelectedTemplate {
            get { return base.GetValue(SelectedTemplateProperty) as Template; }
            set { base.SetValue(SelectedTemplateProperty, value); }
        }

        /// <summary>
        /// Initializes a new <see cref="WinAddTemplate"/>.
        /// </summary>
        public WinAddTemplate() {
            InitializeComponent();
            SelectedTemplate = new Template();
        }

        /// <summary>
        /// Initializes a new <see cref="WinAddTemplate"/> to edit an template.
        /// </summary>
        /// <param name="template">The template to edit.</param>
        public WinAddTemplate(ITemplate template) 
            :this() {
            this.SelectedTemplate = template;
            _originalName = template.Name;
            _editing = true;
            Title = I18nUtils.GetString("Strings", "EditTemplate");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e) {
            txtName.Focus();
            txtName.SelectAll();
        }

        /// <summary>
        /// Checks the data entered is valid.
        /// </summary>
        /// <returns><c>True</c> if the data is valid, otherwise <c>false</c></returns>
        private bool Validate() {
            bool valid = true;
            if(txtName.Text.Trim().Length == 0) {
                DialogBox.ShowAlert(this, I18nUtils.GetString("Errors", "AccountTypeRequired"), I18nUtils.GetString("Errors", "Error"));
                txtName.Focus();
                valid = false;
            } else {
                Dictionary<string, int> nameCount = new Dictionary<string, int>();
                foreach(IField field in SelectedTemplate.Fields) {
                    if(nameCount.ContainsKey(field.Name)) {
                        nameCount[field.Name] = nameCount[field.Name] + 1;
                    } else {
                        nameCount[field.Name] = 1;
                    }
                }
                IEnumerable<string> duplicates = nameCount.Where(c => c.Value > 1).Select(c => c.Key);
                if(duplicates.Any()) {
                    valid = false;
                    DialogBox.ShowAlert(this, I18nUtils.GetString("Errors", "SomeFieldsDuplicated") + ": " + duplicates.Aggregate((names, name) => names + ", " + name), I18nUtils.GetString("Errors", "Error"));
                }

                // check for unique name
                BackgroundWorker worker = new BackgroundWorker();
                ITemplate template = SelectedTemplate.Clone();
                worker.DoWork += (s, dwe) => dwe.Result = DalConfigurationManager.Provider.GetTemplatesByName(template.Name);
                worker.RunWorkerCompleted += (s, rwe) => {
                    if(rwe.Error != null) {
                        DialogBox.ShowAlert(this, rwe.Error.Message, I18nUtils.GetString("Errors", "Error"));
                    } else {
                        var existingTemplates = rwe.Result as IEnumerable<ITemplate>;
                        if(existingTemplates != null) {
                            int numExisting = existingTemplates.Count();
                            if((!_editing && numExisting > 0) || (_editing && numExisting > 0 && _originalName != template.Name)) {
                                DialogBox.ShowAlert(this, I18nUtils.GetString("Errors", "TemplateNameExists"), I18nUtils.GetString("Errors", "Error"));
                                valid = false;
                            }
                        }
                    }
                };
                worker.RunWorkerAsync();
                while(worker.IsBusy) {
                    Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate() { }));
                }
            }
            return valid;
        }

        /// <summary>
        /// Saves the template.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void Save(object sender, ExecutedRoutedEventArgs e) {
            if(Validate()) {
                /*BackgroundWorker worker = new BackgroundWorker();
                ITemplate template = SelectedTemplate.Clone();
                if(_editing) {
                    worker.DoWork += (s, dwe) => DalConfigurationManager.Provider.EditTemplate(template);
                } else {
                    worker.DoWork += (s, dwe) => DalConfigurationManager.Provider.AddTemplates(new[] { template }, null);
                }
                worker.RunWorkerCompleted += (s, rwe) => {
                    if(rwe.Error != null) {
                        DialogBox.ShowAlert(this, rwe.Error.Message, I18nUtils.GetString("Errors", "Error"));
                    } else {
                        DialogResult = true;
                    }
                };
                worker.RunWorkerAsync();*/
                DialogResult = true;
            }
        }

        /// <summary>
        /// Determines whether a field can be moved by the specified sender.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanMoveField(object sender, CanExecuteRoutedEventArgs e) {
            if(!IsLoaded)
                return;
            FrameworkElement lbl = e.OriginalSource as FrameworkElement;
            if(lbl == null)
                return;
            Field field = lbl.DataContext as Field;
            int direction = int.Parse((e.Parameter ?? "0").ToString());
            if(direction > 0) { // move down
                e.CanExecute = field.SortIndex < SelectedTemplate.Fields.Count() - 1;
            } else if(direction < 0) {
                e.CanExecute = field.SortIndex > 0;
            }
        }

        /// <summary>
        /// Moves a field up or down.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void MoveField(object sender, ExecutedRoutedEventArgs e) {
            FrameworkElement lbl = e.OriginalSource as FrameworkElement;
            if(lbl == null)
                return;
            Field field = lbl.DataContext as Field;
            int direction = int.Parse((e.Parameter ?? "0").ToString());
            LockCryptBLL.MoveField(field, SelectedTemplate.Fields, direction);

            // should really add a sort method to the collection class
            var currentFields = SelectedTemplate.Fields;
            SelectedTemplate.Fields = new SortedObservableCollection<IField>(new Comparers.FieldComparer());
            foreach(var f in currentFields) {
                SelectedTemplate.Fields.Add(f);
            }
        }

        /// <summary>
        /// Adds a new field.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void AddField(object sender, ExecutedRoutedEventArgs e) {
            WinInputDialog dlg = new WinInputDialog(I18nUtils.GetString("Strings", "AddField"), I18nUtils.GetString("Strings", "FieldName")) {
                Owner = this,
            };
            dlg.ValidateInput += () => {
                bool valid = true;
                string fieldName = dlg.UserInput.Trim();
                if(fieldName.Length == 0) {
                    DialogBox.ShowAlert(this.Parent as Window, I18nUtils.GetString("Errors", "FieldNameInvalid"), I18nUtils.GetString("Errors", "Error"));
                    valid = false;
                } else {
                    IField matchingFieldName = SelectedTemplate.Fields.FirstOrDefault(f => f.Name.Equals(fieldName, StringComparison.InvariantCultureIgnoreCase));
                    if(matchingFieldName != null) { // field with the same name exists
                        DialogBox.ShowAlert(this.Parent as Window, I18nUtils.GetString("Errors", "FieldExists"), I18nUtils.GetString("Errors", "FieldNameInvalid"));
                        valid = false;
                    }
                }
                return valid;
            };
            if(dlg.ShowDialog() == true) {
                var newField = new Field() {
                    Name = dlg.UserInput.Trim(),
                    SortIndex = SelectedTemplate.Fields.Count
                };
                SelectedTemplate.Fields.Add(newField);
                //AddRow(newField);
            }
        }

        /// <summary>
        /// Removes a field.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void DeleteField(object sender, ExecutedRoutedEventArgs e) {
            FrameworkElement lbl = e.OriginalSource as FrameworkElement;
            if(lbl == null)
                return;
            Field field = lbl.DataContext as Field;
            IEnumerable<IField> affectedFields = SelectedTemplate.Fields.Where(f => f.SortIndex > field.SortIndex);
            foreach(IField affectedField in affectedFields) {
                affectedField.SortIndex--;
            }

            SelectedTemplate.Fields.Remove(field);
        }

        /// <summary>
        /// Marks a field as a URL.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void MarkAsUrl(object sender, ExecutedRoutedEventArgs e) {
            FrameworkElement lbl = e.OriginalSource as FrameworkElement;
            if(lbl == null)
                return;
            Field field = lbl.DataContext as Field;
            LockCryptBLL.ToggleMetaFlag(field, FieldMetaData.Url);
        }

        /// <summary>
        /// Marks a field as a password.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void MarkAsPassword(object sender, ExecutedRoutedEventArgs e) {
            FrameworkElement lbl = e.OriginalSource as FrameworkElement;
            if(lbl == null)
                return;
            Field field = lbl.DataContext as Field;
            LockCryptBLL.ToggleMetaFlag(field, FieldMetaData.Password);
        }

        /// <summary>
        /// Marks a field as a phone number.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void MarkAsPhone(object sender, ExecutedRoutedEventArgs e) {
            FrameworkElement lbl = e.OriginalSource as FrameworkElement;
            if(lbl == null)
                return;
            Field field = lbl.DataContext as Field;
            LockCryptBLL.ToggleMetaFlag(field, FieldMetaData.Phone);
        }

        /// <summary>
        /// Closes the window.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void CloseWindow(object sender, ExecutedRoutedEventArgs e) {
            DialogResult = false;
        }

        /// <summary>
        /// Shows the help file.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ShowHelp(object sender, ExecutedRoutedEventArgs e) {
            WPFUtils.OpenHelp("Templates.html");
        }
    }
}
